﻿function CRUDViewModel(model) {
    var selfModel = this;

    var disabledPages = [model.Controller + "Read", model.Controller + "Delete"];

    var initialize = function (formId) {
        if (formId) {
            if (!$("#" + formId))
                throw error("Please, add 'create' form");

            if (!$("#" + formId).attr("action"))
                throw error("Please, add action to create form");
        }
    };

    var invoke = function (formId, data) {
        var action = $("#" + formId).attr("action");
        var method = $("#" + formId).attr("method");

        ui.Animation.ShowLoading();
        var request = $.ajax({
            url: action,
            type: method,
            data: ko.toJSON(data),
            cache: false,
            contentType: 'application/json',
            dataType: "json"
        });

        return request;
    };

    var createMethod = function (data) {
        initialize(model.Controller + "Create");
        return invoke(model.Controller + "Create", data);
    };

    var updateMethod = function (data) {
        initialize(model.Controller + "Update");
        return invoke(model.Controller + "Update", data);
    };

    var deleteMethod = function (data) {
        initialize(model.Controller + "Delete");
        return invoke(model.Controller + "Delete", data);
    };

    model.EmptyEntity = ko.toJS(model.Entity);
    model.SelectedItem = ko.observable();

    model.base = {
        Init: function (isMain) {
            Main.State.IsCRUD(true);

            Main.State.Hashes.push(model.Controller + "Create");
            Main.State.Hashes.push(model.Controller + "Read");
            Main.State.Hashes.push(model.Controller + "Update");
            Main.State.Hashes.push(model.Controller + "Delete");

            if (isMain)
                Main.Page.Init(model.StartPage, model.Controller);

            if (Main.State.TryIndexing())
                model.LoadIndexed(model, Main.State.IndexedId());

            ui.Form.FormEnabling(Main.State.CurrentPage(), disabledPages);

            Main.State.CurrentPage.subscribe(function (newPage) {
                ui.Form.FormEnabling(newPage, disabledPages);

                if (newPage == "Create")
                    model.ResetForm();
            });
        },
        SelectItem: function (action, item) {
            model.SelectedItem(item);

            Main.Page.ChangeHash(action + "/" + item.Id());
        },
        RebindList: function () {
            var method = whoever.AJAX.Method({
                data: {},
                action: "../" + model.Controller + "/RebindList"
            });

            method.success(function (list) {
                ko.mapping.fromJS(list, {}, model.List);
            });
        },
        LoadIndexed: function (viewModel, id) {
            if (!viewModel.SelectedItem) console.error("SelectedItem is required");
            if (!viewModel.List) console.error("List is required");

            var item = whoever.Observables.GetBy(viewModel.List(), "Id", id)[0];

            if (viewModel.SelectItem)
                viewModel.SelectItem(Main.State.CurrentPage(), item);
            else
                viewModel.SelectedItem(item);
        },
        ResetForm: function () {
            ko.mapping.fromJS(model.EmptyEntity, {}, model.Entity);
            whoever.Form.EnableAll();
        },
        Create: function () {
            if (Main.Validation.ModelIsValid(model.Entity)) {
                if (model.Create.SuccessCallBack)
                    model.Create.SuccessCallBack();
                var method = createMethod(model.Entity);

                method.success(function (result) {
                    model.RebindList();

                    ui.Animation.HideLoading(function () {
                        var options = [];

                        options.Actions = [
                            { Text: "Crear otro", CssClass: result.Success ? "btn btn-success" : "btn btn-warning", Behavior: model.ResetForm },
                            { Text: "Volver", CssClass: "btn btn-default", Behavior: function () { Main.Page.ChangeHash(model.Controller + "Info"); } }
                        ];

                        options.MessageType = result.MessageType;
                        options.Message = result.Message;

                        ui.Message.MessageWithAction(options);

                        whoever.Form.EnableBack();
                    });
                });

                method.error(function () {
                    if (model.Create.ErrorCallBack)
                        model.Create.ErrorCallBack();

                    ui.Animation.HideLoading(ui.Message.Error(errorMessage));
                });
            } else {
                if (model.Create.ModelErrorCallBack)
                    model.Create.ModelErrorCallBack();

                ui.Message.Warning(invalidModelMessage);
            }
        },
        Update: function () {
            if (Main.Validation.ModelIsValid(model.SelectedItem())) {
                if (model.Update.SuccessCallBack)
                    model.Update.SuccessCallBack();

                var method = updateMethod(model.SelectedItem());

                method.success(function (result) {
                    ui.Animation.HideLoading(function () {
                        var options = [];

                        options.Actions = [
                            { Text: "Seguir editando", CssClass: result.Success ? "btn btn-success" : "btn btn-warning", Behavior: whoever.Form.EnableAll },
                            { Text: "Volver", CssClass: "btn btn-default", Behavior: function () { Main.Page.ChangeHash(model.Controller + "Info"); } }
                        ];

                        options.MessageType = result.MessageType;
                        options.Message = result.Message;

                        ui.Message.MessageWithAction(options);

                        whoever.Form.EnableBack();
                    });
                });

                method.error(function () {
                    if (model.Update.ErrorCallBack)
                        model.Update.ErrorCallBack();

                    ui.Animation.HideLoading(ui.Message.Error(errorMessage));
                });
            } else {
                if (model.Update.ModelErrorCallBack)
                    model.Update.ModelErrorCallBack();

                ui.Message.Warning(invalidModelMessage);
            }
        },
        Delete: function () {
            if (Main.Validation.ModelIsValid(model.SelectedItem())) {
                if (model.Delete.SuccessCallBack)
                    model.Delete.SuccessCallBack();

                var method = deleteMethod(model.SelectedItem());

                method.success(function (result) {
                    model.RebindList();

                    ui.Animation.HideLoading(function () {
                        var options = [];

                        options.Actions = [
                            { Text: "Volver", CssClass: "btn btn-default", Behavior: function () { Main.Page.ChangeHash(model.Controller + "Info"); } }
                        ];

                        options.MessageType = result.MessageType;
                        options.Message = result.Message;

                        ui.Message.MessageWithAction(options);

                        whoever.Form.EnableBack();
                    });
                });

                method.error(function () {
                    if (model.Delete.ErrorCallBack)
                        model.Delete.ErrorCallBack();

                    ui.Animation.HideLoading(ui.Message.Error(errorMessage));
                });
            } else {
                if (model.Delete.ModelErrorCallBack)
                    model.Delete.ModelErrorCallBack();

                ui.Message.Warning(invalidModelMessage);
            }
        }
    };

    //Model exposure

    model.Init = function (isMain) {
        model.base.Init(isMain);
    };

    model.SelectItem = function (action, item) {
        model.base.SelectItem(action, item);
    };

    model.RebindList = function () {
        model.base.RebindList();
    };

    model.LoadIndexed = function (viewModel, id) {
        model.base.LoadIndexed(viewModel, id);
    };

    model.ResetForm = function () {
        model.base.ResetForm();
    };

    model.Create = function () {
        model.base.Create();
    };

    model.Update = function () {
        model.base.Update();
    };

    model.Delete = function () {
        model.base.Delete();
    };
}